function Watcher(vm, exp, cb) {
  this.cb = cb
  this.vm = vm
  this.exp = exp

  // 这个depIds用于存储数据对应的dep实例
  this.depIds = {}
  // 2. 这里开始绑定dep和watcher获取msg的值
  this.value = this.get()
}

Watcher.prototype = {
  // 17. 要更新页面了,所以继续调用run方法,往下执行
  update: function () {
    this.run()
  },
  run: function () {
    // 18.从新获取最新数据
    var value = this.get()
    // 获取之前存储的上一次的旧数据
    var oldVal = this.value
    // 判断新数据是否和旧数据相同,相同则什么都不做
    if (value !== oldVal) {
      // 将新数据存储起来,作为下一次的旧数据
      this.value = value
      // 19. 调用new Watcher时的回调函数,让模板更新
      this.cb.call(this.vm, value, oldVal)
    }
  },
  //8. 判断watcher中之前有没有存储过对应的dep实例,没有则添加
  addDep: function (dep) {
    if (!this.depIds.hasOwnProperty(dep.id)) {
      // 9. 将watcher实例存储到dep实例的subs这个数组中
      dep.addSub(this)
      // 将dep实例存储到watcher实例的depIds这个对象中,其实dep的id就是键,dep实例就是值
      this.depIds[dep.id] = dep
    }
  },
  get: function () {
    Dep.target = this
    // 3. 调用getVMVal获取msg的值
    var value = this.getVMVal()
    Dep.target = null
    return value
  },

  getVMVal: function () {
    var exp = this.exp.split('.')
    var val = this.vm._data
    exp.forEach(function (k) {
      // 4. 获取msg的值,触发数据劫持的get
      val = val[k]
    })
    return val
  },
}
